package com.ly.wxstore.service.account;

import java.io.IOException;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.ly.wxstore.comm.Error;
import com.ly.wxstore.service.account.ShiroDbRealm.ShiroUser;

/**
 * <ul>
 * 		<li>该filter的作用类似于FormAuthenticationFilter用于oauth2客户端的身份验证控制；</li>
 * 		<li>如果当前用户还没有身份验证，首先会判断url中是否有code（服务端返回的auth code）， 如果没有则重定向到服务端进行登录并授权，然后返回authcode；</li>
 * 		<li>接着OAuth2AuthenticationFilter会用auth code创建OAuth2Token，然后提交给Subject.login进行登录；</li>
 * 		<li>接着OAuth2Realm会根据OAuth2Token进行相应的登录逻辑。</li>
 * </ul>
 * @author Peter
 * 
 */
@Component
public class WeixinOAuthAuthenticationFilter extends AuthenticatingFilter {
	private static Logger logger = LoggerFactory.getLogger(WeixinOAuthAuthenticationFilter.class);

	/**
	 * errcode
	 */
	public static final String ERROR_CODE_PARAM = "errcode";

	/**
	 * errmsg
	 */
	public static final String ERROR_MSG_PARAM = "errmsg";

	// oauth2 authc code参数名
	private String authcCodeParam = "code";
	// 客户端id
	private String clientId;
	// 服务器端登录成功/失败后重定向到的客户端地址
	private String redirectUrl;
	// oauth2服务器响应类型
	private String responseType = "code";
	private String failureUrl;

	@Override
	protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		String code = httpRequest.getParameter(authcCodeParam);
		String state = httpRequest.getParameter("state"); // 重定向后会带上state参数，开发者可以填写a-zA-Z0-9的参数值，最多128字节
		String host = getHost(httpRequest);
		logger.info("createToken-->>{code:" + code + ",state:" + state + ",host:" + host + "}");

		return new WeixinOAuthToken(code, state, false, host);
	}

	/**
	 * 用户访问拒绝时进入该方法（如用户未登录）
	 */
	@Override
	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
		HttpServletRequest httpRequest = (HttpServletRequest)request;
		logger.info("onAccessDenied    RequestURL-->>"+httpRequest.getRequestURL() +"?"+httpRequest.getQueryString());
		
		String errcode = request.getParameter("errcode");
		String errmsg = request.getParameter("errmsg");
		if (!StringUtils.isEmpty(errcode)) {// 如果服务端返回了错误
			logger.info("onAccessDenied-->>{errcode:" + errcode + ",errmsg:" + errmsg + "}");
			WebUtils.issueRedirect(request, response, failureUrl + "?" + ERROR_CODE_PARAM + "=" + errcode + "&errmsg=" + errmsg);
			return false;
		}
		Subject subject = getSubject(request, response);
		if (!subject.isAuthenticated()) {
			if (StringUtils.isEmpty(request.getParameter(authcCodeParam))) {
				// 如果用户没有身份验证，且没有auth code，则重定向到服务端授权
				saveRequestAndRedirectToLogin(request, response);
				return false;
			}
		}
		// 执行父类里的登录逻辑，调用Subject.login登录
		return executeLogin(request, response);
	}

	// 登录成功后的回调方法 重定向到成功页面
	@Override
	protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
		ShiroUser user = (ShiroUser) subject.getPrincipal();
		logger.info("onLoginSuccess-->>{openid:" + user.getOpenid() + ",sid" + user.getSid() + "}");
		issueSuccessRedirect(request, response);
		return false;
	}

	// 登录失败后的回调
	@Override
	protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException ae, ServletRequest request, ServletResponse response) {
		logger.info("onLoginFailure-->>{message:" + ae.getMessage() + "}");
		Subject subject = getSubject(request, response);
		if (subject.isAuthenticated() || subject.isRemembered()) {
			try { // 如果身份验证成功了 则也重定向到成功页面
				issueSuccessRedirect(request, response);
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else {
			try { // 登录失败时重定向到失败页面
				WebUtils.issueRedirect(request, response,
						failureUrl + "?" + ERROR_CODE_PARAM + "=" + Error._20002 + "&errmsg=" + Error._20002_MSG + " - " + ae.getMessage());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return false;
	}

	// ----------getter and setter ---------------------------------------------
	public String getAuthcCodeParam() {
		return authcCodeParam;
	}

	public void setAuthcCodeParam(String authcCodeParam) {
		this.authcCodeParam = authcCodeParam;
	}

	public String getClientId() {
		return clientId;
	}

	public void setClientId(String clientId) {
		this.clientId = clientId;
	}

	public String getRedirectUrl() {
		return redirectUrl;
	}

	public void setRedirectUrl(String redirectUrl) {
		this.redirectUrl = redirectUrl;
	}

	public String getResponseType() {
		return responseType;
	}

	public void setResponseType(String responseType) {
		this.responseType = responseType;
	}

	public String getFailureUrl() {
		return failureUrl;
	}

	public void setFailureUrl(String failureUrl) {
		this.failureUrl = failureUrl;
	}

}
